package com.zelic.zwrite;

import android.content.*;
import android.graphics.*;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.util.*;
import android.view.*;
import java.util.*;

public class WriteView extends View {

	private ArrayList<Path> paths;
	private ArrayList<PointF> points;

	// This is what I last added
	private ArrayList<ArrayList<Point>> listStrokes;
	private ArrayList<Point> currentStrokes;

	private final int NumPoints = 21;
	private int NumStrokes = 0;

	private Paint paint;
	private Path currentPath;
	private float oldX, oldY;


	private void setup() {
		setupView();
		paint = new Paint();
		paint.setColor(Color.BLACK);
		paint.setAntiAlias(true);
		paint.setDither(true);
		paint.setStyle(Style.STROKE);
		paint.setStrokeCap(Cap.ROUND);
		paint.setStrokeJoin(Join.ROUND);
		paint.setStrokeWidth(2);
		this.setDrawingCacheBackgroundColor(Color.WHITE);
		listStrokes = new ArrayList<ArrayList<Point>>();
	}
	
	private void setupView(){
		oldX = 0;
		oldY = 0;
		currentPath = null;
		paths = new ArrayList<Path>();

		points = new ArrayList<PointF>();

		listStrokes = new ArrayList<ArrayList<Point>>();

		this.invalidate();
	}

	public void resetView() {
		oldX = 0;
		oldY = 0;
		currentPath.reset();
		paths.clear();

		points.clear();

		listStrokes.clear();
		NumStrokes = 0;
		this.invalidate();
	}

	public WriteView(Context context) {
		super(context);
		setup();

	}

	public WriteView(Context context, AttributeSet attrs) {
		super(context, attrs);
		setup();
	}

	public WriteView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		setup();
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawColor(Color.WHITE);
		if (paths != null && points != null) {
			for (Path path : paths) {
				if (path != null) {
					canvas.drawPath(path, paint);
				}
			}

			for (PointF point : points) {
				canvas.drawPoint(point.x, point.y, paint);
			}
		}

	}

	public double[] getStrokesVector() {
		ArrayList<Vector> vector = new ArrayList<Vector>();
		double[] result = new double[NumPoints*2-1];
		vector = normalize(vectorize(extractPoints(listStrokes)));
		result[0] = NumStrokes;
		for (int i = 1; i <= NumPoints * 2-2; i++) {
			if (i % 2 == 1) {
				result[i] = vector.get((i - 1) / 2).getX();
			} else {
				result[i] = vector.get((i - 1) / 2).getY();
			}
		}
		return result;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX();
		float y = event.getY();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			currentStrokes = new ArrayList<Point>();
			listStrokes.add(currentStrokes);
			currentStrokes.add(new Point((int) x, (int) y));
			NumStrokes++;
			currentPath = new Path();
			currentPath.moveTo(x, y);
			oldX = x;
			oldY = y;
			paths.add(currentPath);
			invalidate();
			break;

		case MotionEvent.ACTION_MOVE:
			currentStrokes.add(new Point((int) x, (int) y));
			if ((Math.abs(x - oldX) > 0) || (Math.abs(y - oldY) > 0)) {
				currentPath.quadTo(oldX, oldY, (oldX + x) / 2, (oldY + y) / 2);
				oldX = x;
				oldY = y;
			}
			invalidate();
			break;

		case MotionEvent.ACTION_UP:
			currentStrokes.add(new Point((int) x, (int) y));
			currentPath.lineTo(oldX, oldY);
			if ((x == oldX) && (y == oldY)) {
				points.add(new PointF(x, y));
			}
			invalidate();

			break;
		}
		return true;
	}

	private ArrayList<Point> extractPointsFromStroke(ArrayList<Point> stroke,
			int num) {
		ArrayList<Point> result = new ArrayList<Point>();
		int srcNumber = stroke.size();
		double step = srcNumber / (double) num;
		for (double i = 0; i < srcNumber - 1 && result.size() < num - 1; i = i
				+ step) {
			result.add(stroke.get((int) Math.ceil(i)));
		}
		result.add(stroke.get(srcNumber - 1));
		return result;
	}

	private ArrayList<Point> extractPoints(ArrayList<ArrayList<Point>> strokes) {
		ArrayList<Point> result = new ArrayList<Point>();
		int total = totalPoints(strokes);
		int select = 0;
		for (int i = 0; i < strokes.size() - 1; i++) {
			select = (int) Math.ceil(strokes.get(i).size() * NumPoints
					/ (double) total);
			result.addAll(extractPointsFromStroke(strokes.get(i), select));
		}
		select = NumPoints - result.size();
		result.addAll(extractPointsFromStroke(strokes.get(strokes.size() - 1),
				select));

		return result;
	}

	private int totalPoints(ArrayList<ArrayList<Point>> strokes) {
		int result = 0;
		for (ArrayList<Point> l : strokes) {
			result += l.size();
		}
		return result;
	}

	private ArrayList<Vector> vectorize(ArrayList<Point> listPoints) {
		ArrayList<Vector> result = new ArrayList<Vector>();
		for (int i = 1; i < listPoints.size(); i++) {
			result.add(new Vector(listPoints.get(i - 1), listPoints.get(i)));
		}
		return result;
	}

	private ArrayList<Vector> normalize(ArrayList<Vector> listStrokes) {
		ArrayList<Vector> result = listStrokes;
		double max = Double.MIN_VALUE;

		for (Vector v : result) {
			double temp = maxLength(v);
			if (max < temp) {
				max = temp;
			}
		}
		for (Vector v : result) {
			v.setX(v.getX() / max);
			v.setY(v.getY() / max);
		}
		return result;
	}

	private double maxLength(Vector vector) {
		if (vector.getX() > vector.getY()) {
			return vector.getX();
		}
		return vector.getY();
	}

}
